package semant.second_pass;

import semant.Env;
import symbol.Symbol;
import symbol.ClassInfo;
import syntaxtree.Type;
import syntaxtree.BooleanType;
import syntaxtree.IntegerType;
import syntaxtree.IntArrayType;
import syntaxtree.IdentifierType;

final class TypeHandler {

    private Env env;

    private TypeHandler(Env e){
	super();
	env = e;
    }

    // testa compatibilidade de tipos
    // retorna true se source eh subtipo de target
    static boolean compatible(Env e, Type target, Type source){
	// tipos primitivos
	if(target instanceof IntegerType)
	    return source instanceof IntegerType;
	else if(target instanceof IntArrayType)
	    return source instanceof IntArrayType;
	else if(target instanceof BooleanType)
	    return source instanceof BooleanType;

	if(!(source instanceof IdentifierType))
	    return false;

	// testa compatibilidade de tipos de objetos
	Symbol tname = Symbol.symbol( ((IdentifierType)target).name );
	Symbol sname = Symbol.symbol( ((IdentifierType)source).name );

	ClassInfo tc = e.classes.get(tname);
	ClassInfo sc = e.classes.get(sname);

	if(tc == null)
	    return false;

	// sc deve ser subtipo de tc
	while(sc != null)
	    if(sc.name == tc.name)
		return true;
	    else
		sc = sc.base;

	// nao encontrou tc na hierarquia
	return false;
    }

    // testa se uma heranca entre duas classes eh valida
    static boolean canInheritFrom(ClassInfo base, ClassInfo derived){
	while(base != null){
	    if(base.name == derived.name)
		return false;
	    else
		base = base.base;
	}
	return true;
    }

    // testa igualdade de tipos
    static boolean equal(Type t1, Type t2){
	// tipos primitivos
	if(t1 instanceof IntegerType)
	    return t2 instanceof IntegerType;
	else if(t1 instanceof IntArrayType)
	    return t2 instanceof IntArrayType;
	else if(t1 instanceof BooleanType)
	    return t2 instanceof BooleanType;

	if(t1 instanceof IdentifierType && t2 instanceof IdentifierType){
	    IdentifierType tt1 = (IdentifierType)t1;
	    IdentifierType tt2 = (IdentifierType)t2;
	    return (tt1.name.equals(tt2.name));
	}

	return false;
    }

    static void secondPass(Env e, Type t){
	if(t instanceof IdentifierType){
	    // verifica se a classe foi declarada
	    IdentifierType it = (IdentifierType)t;
	    ClassInfo cinfo = e.classes.get(Symbol.symbol(it.name));
	    if(cinfo == null)
		e.err.Error(it, new Object[]{"Classe '" + it.name + "' nao foi declarada"});
	}
    }

}
